using Century.EnterpriseLibrary.Configuration;
using log4net;
using System.IO;
using System.Net;


namespace Century.EnterpriseLibrary.GeoIP
{
	public sealed class GeoIPService : ICountrySearch, System.IDisposable
	{
        #region [ Singletone Instance ]

        public static readonly GeoIPService instance = new GeoIPService();

        #endregion

        #region [ Constructor(s) ]

        private GeoIPService()
        {

            this.databaseFile = new FileInfo(System.Web.HttpContext.Current.Server.MapPath(ConfManager.Instance.GeoIP.GeoIPDbPath));
            try
            {
                this.serviceImplementation = new MaxMindCountrySearch(this.ReadDatabase());
            }
            catch (System.Exception exception)
            {
                Log.Error("Failed to load geoip database", exception);
                this.serviceImplementation = new StubGeoIPService();
            }
            this.watchTimer = new System.Threading.Timer(new System.Threading.TimerCallback(this.OnDatabaseChanged), null, -1, -1);
            try
            {
                this.watcher = new FileSystemWatcher(databaseFile.DirectoryName, databaseFile.Name)
                {
                    NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime
                };
                this.watcher.Changed += new FileSystemEventHandler(this.OnDatabaseChanged);
                this.watcher.Created += new FileSystemEventHandler(this.OnDatabaseChanged);
                this.watcher.Deleted += new FileSystemEventHandler(this.OnDatabaseChanged);
                this.watcher.Renamed += new RenamedEventHandler(this.OnDatabaseRenamed);
                this.watcher.EnableRaisingEvents = true;
            }
            catch (System.ArgumentException exception2)
            {
                Log.Error("Could not created watcher for change tracking. It means that specified directory does not exist. Please correct path to the database file.", exception2);
            }
        }

        #endregion

		private const int MaxDatabaseSize = 2147483591;

        private ILog Log = LogManager.GetLogger("GeoIPService");
		private ICountrySearch serviceImplementation;

        private readonly System.IO.FileInfo databaseFile;
		private readonly FileSystemWatcher watcher;
		private readonly System.Threading.Timer watchTimer;
		
		private byte[] ReadDatabase()
		{
			if (this.databaseFile.Length > 2147483591L)
			{
				throw new System.InvalidOperationException("Size of database file is greater than the maximum size allowed.");
			}
			System.IO.FileInfo fileInfo = new System.IO.FileInfo(this.databaseFile.FullName);
			int num = (int)fileInfo.Length;
			byte[] array = new byte[num];
			using (System.IO.FileStream fileStream = fileInfo.OpenRead())
			{
				fileStream.Read(array, 0, num);
			}
			return array;
		}
		private void OnDatabaseChanged(object state)
		{
			try
			{
				byte[] countryDatabase = this.ReadDatabase();
				if (this.serviceImplementation is MaxMindCountrySearch)
				{
					((MaxMindCountrySearch)this.serviceImplementation).UpdateDatabase(this.ReadDatabase());
				}
				else
				{
					MaxMindCountrySearch maxMindCountrySearch = new MaxMindCountrySearch(countryDatabase);
					this.serviceImplementation = maxMindCountrySearch;
				}
				Log.Debug("GeoIP database updated");
			}
			catch (System.Exception exception)
			{
				Log.Error("Error on updating MaxMindCountrySearch", exception);
			}
		}
		private void OnDatabaseChanged(object sender, FileSystemEventArgs e)
		{
			this.watchTimer.Change(500, -1);
		}
		private void OnDatabaseRenamed(object source, RenamedEventArgs e)
		{
			this.watchTimer.Change(500, -1);
		}
		public string GetCountryCode(string ipAddress)
		{
			string result;
			try
			{
				result = this.serviceImplementation.GetCountryCode(ipAddress);
			}
			catch (System.Exception exception)
			{
				Log.Error("Error when detecting country by IP:", exception);
				result = string.Empty;
			}
			return result;
		}
		public string GetCountryCode(IPAddress address)
		{
			string result;
			try
			{
				result = this.serviceImplementation.GetCountryCode(address);
			}
			catch (System.Exception exception)
			{
				Log.Error("Error when detecting country by IP:", exception);
				result = string.Empty;
			}
			return result;
		}
		public void Dispose()
		{
			try
			{
				this.watchTimer.Dispose();
			}
			catch (System.Exception exception)
			{
				Log.Error("Error on disposing watching timer", exception);
			}
			try
			{
				this.watcher.Dispose();
			}
			catch (System.Exception exception2)
			{
				Log.Error("Error on disposing database file watcher", exception2);
			}
			this.serviceImplementation = null;
		}
	}
}
